

/**
 * I/O routines for the swarm library. Stuff like reading files defining
 * a swarm.
 */

#include <swarm.h>
#include <swarmio.h>
#include <sw_signal.h>

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

swio_symbol_table *sym_tbl;
swio_file_stack *files;

/**
 * This gets a line from the file on top of the file stack. If NULL is read,
 * NULL is returned. This also trims white space and comments.
 */
char line[2048];
char *_swio_get_next_line(){

  memset(line, 0, 2048);
  char *ok = fgets(line, 512, files->top->file);
  if (!ok){
    return NULL;
  }

  /* Determine if theres a comment here and cut it off. */
  int i = 0;
  for ( ; i < strlen(line); i++){
    if (line[i] == '#'){
      line[i] = 0;
      break;
    }
  }

  /* Cut of the ending white space */
  int end_non_wspace = strlen(line) - 1;
  for ( ; end_non_wspace >= 0; end_non_wspace--){
    if (isspace(line[end_non_wspace])){
      line[end_non_wspace] = 0;
      //printf("[swarmio] Trimmed white space.\n");
    } else {
      break;
    }
  }

  /* Cut of any beginning white space. */
  char *cut_line = line;
  while (isspace(*cut_line)) cut_line++;

  /* Malloc a string of the right size here. */
  char *real_line = (char *)malloc(strlen(cut_line)+1);
  memcpy(real_line, cut_line, strlen(cut_line)+1);
  //printf("|%s|\n", real_line);

  files->top->current_line++;
  return real_line;

}

char *swio_get_next_line(){

  char *line = _swio_get_next_line();
  if (line == NULL) return NULL;

  /* See if we have to do some appeneding. */

  if (line[strlen(line) - 1] == '\\'){
    while ( line[strlen(line) - 1] == '\\'){
      
      /* Clean the line up. */
      line[strlen(line) - 1] = 0;
      int top = strlen(line) - 1;
      for ( ; top > 0; top--){
	if ( isspace(line[top-1])) line[top] = 0;
	else break;
      }

      /* Now append the next line onto this line. */
      char *next_line = _swio_get_next_line();
      char *appended = (char *) malloc(strlen(line) + strlen(next_line) + 1);
      
      memcpy(appended, line, strlen(line) + 1);
      strncat(appended, next_line, strlen(line) + strlen(next_line) + 1);
      free(line);
      line = appended;

    }

    /* Final clean of the line up. */
    int top = strlen(line) - 1;
    for ( ; top > 0; top--){
      if ( isspace(line[top])) line[top] = 0;
      else break;
    }

  }

  return line;

}

/**
 * Rebuilds a string with just the section quoted. The quotes are removed too.
 */
char *_swio_trim_quotes(char *line){

  while (*line++ != '"'){
    if (*line == 0) return NULL;
  }

  /* Now find and remove the end quote. */
  int top = strlen(line) - 1;
  for ( ; top > 0; top--){
    if (line[top] == '"'){
      line[top] = 0;
      return line;
    }
  }

  return NULL;

}

void swio_die_miss_matched_quote(char *val){

  printf("[swarmio] Missing closing quote: %s\n", val);
  printf("[swarmio]   File: %s, line: %d\n",
	 files->top->name, files->top->current_line);
  sw_exit(1);

}

void _swio_file_stack_push(swio_file_frame *frame){

  frame->prev = files->top;
  files->top = frame;
  files->length++;

}

void _swio_file_stack_pop(){

  if (!files->top) return;
  files->length--;
  files->top = files->top->prev;

}

swio_file_frame *_swio_make_file_frame(char *name){

  swio_file_frame *new_frame = 
    (swio_file_frame *)malloc(sizeof(swio_file_frame));
  memset(new_frame, 0, sizeof(swio_file_frame));

  /* Open the file. */
  new_frame->file = fopen(name, "r");
  if (!new_frame->file) { // The file open failed.
    free(new_frame);
    return NULL;
  }

  new_frame->name = name;

  return new_frame;

}

/**
 * Creates the symbol table.
 */
void swio_init(){

  if(!sym_tbl){
    sym_tbl = hash_new(30);
  }

  if (!files){
    files = (swio_file_stack *) malloc(sizeof(swio_file_stack));
    memset(files, 0, sizeof(swio_file_stack));
  }

  sw_signal(SIGSEGV, sw_signal_io_sigsegv);

  printf("[swarmio] init'ed.\n");

}

int _swio_str_to_command(char *str){

  return 0;

}

char **line_starts;

#define NUM_STARTS 6

void _swio_make_line_starts(){

  line_starts = (char **)malloc(sizeof(char *) * NUM_STARTS);

  line_starts[0] = "include ";
  line_starts[1] = "force ";
  line_starts[2] = "insect ";
  line_starts[3] = "window ";
  line_starts[4] = "sim_name ";
  line_starts[5] = "random ";

}

int _swio_line_start(char *str){

  if (!line_starts){
    _swio_make_line_starts();
  }

  int i = 0;
  for ( ; i < NUM_STARTS; i++){
    if (!strncmp(str, line_starts[i], strlen(line_starts[i])))
      return i;
  }

  return -1;

}

/**
 * Loads a swarm file. In essense a script describing how to make a swarm.
 */
sw_swarm *sw_load_swarm(char *swarm_file){

  if (!swarm_file) return NULL;

  /* Init the parser and file stack. */
  swio_init();
  sw_swarm *sw = sw_swarm_new();
  _sw_set_internal(sw);
  char *current_line = NULL;

  /* Start the file stack. */
  swio_file_frame *start = _swio_make_file_frame(swarm_file);
  if (start){
    _swio_file_stack_push(start);
  } else {
    free(sw);
    return NULL;
  }

  /* This gets broken when the file stack is empty. */
  while (files->length > 0){
    
    /* Parse a file. */
    while ( (current_line = swio_get_next_line()) != NULL){
      
      /* We have a line to work with. Parse it here. */
      if (strlen(current_line) < 1) continue;
      //printf("[swarmio] file: %s line: %s\n", 
	     //files->top->name, current_line);

      /* Check to see if the line starts with something interesting. */
      int line_command = -1;
      if ((line_command = _swio_line_start(current_line)) != -1){
	switch (line_command){
	case 0: // include
	  if (swio_com_include(current_line)){
	    sw_exit(1);
	  }
	  break;
	case 1: // force
	  if (swio_com_force(current_line, sw)){
	    sw_exit(1);
	  }
	  break;
	case 2: // insect
	  if (swio_com_insect(current_line, sw)){
	    sw_exit(1);
	  }
	  break;
	case 3: // window
	  if (swio_com_window(current_line, sw)){
	    sw_exit(1);
	  }
	  break;
	case 4: // sim_name
	  if (swio_com_sim_name(current_line, sw)){
	    sw_exit(1);
	  }
	  break;
	case 5: // random
	  if (swio_com_random(current_line)){
	    sw_exit(1);
	  }
	  break;
	default:
	  printf("[swarmio] This is likely a bug.\n");
	  sw_exit(1);
	}
      } else {
	/* We assume its a symbol of some kind till we learn otherwise. */
	if (swio_symbolize(current_line)){
	  printf("[swarmio] Error parsing line: %s\n", current_line);
	  printf("[swarmio]   Invalid symbol declration.\n");
	  printf("[swarmio]   File: %s, line: %d\n",
		 files->top->name, files->top->current_line);
	  sw_exit(1);
	}
      }
     
    }
    _swio_file_stack_pop();
  }

  _sw_load_sym_table_data(sw);

  return sw;

}

